#include "MultimodalTestApp.h"
#include "CommandHandler.h"
#include "SingleHandTrack.h"

#include <../Users/acer/Downloads/cgwiimote110/cgwiimote/include/cybergarage/wii/Wiimote.h>
using namespace CyberGarage;

/*
 *	TODO TECHNICAL: 
 *
 *	- Accelerate polygon collisions (implement trivial exclusion) >> ACCEPTED
 *	- Ball throw with mouse follow >> ACCEPTED
 *	- Connect World to the command handler >> ACCEPTED
 *	- Polygons need to be able to draw themselves (and update), need a memeber variable for a 'manual entity'
 *	- Option to place/ rotate certain polygons (generalize polygon class, needs center point)
 *	
 *	PHYSICS
 *
 *	- improve ball2ball collsisions (less bounce, fix div/0, better frame lag compensation) 
 *	- implement collision noise (on balls, walls & polygons)
 *	- improve poly collision (needs to know if colliding face is the closest one) >> REJECTED (necessary??)
 *
 *	GAMEPLAY
 *
 *  - fix initial palyer cam position >> ACCEPTED
 *	- Create player and collider sphere >> ACCEPTED
 *	- retsrict pick up zone >> ACCEPTED
 *	- Prevent target sphere from being selectable
 *	- Reset level after target is hit/or missed (timer) >> ACCEPTED
 *	- create visible bounday to restrict 'pick up' area
 *
 */

#define PICKBOX_TOP -50
#define PICKBOX_RIGHT -20

MultimodalTestApp::MultimodalTestApp(void){}

MultimodalTestApp::~MultimodalTestApp(void){}

World* MultimodalTestApp::getWorld(void){return m_world;}

void MultimodalTestApp::createScene(void)
{
	// create world
	m_world = new World(mSceneMgr);
	m_tries = 0;

	//create an output manager
	new OutputManager();

	// create text boxes
	OutputManager::getSingleton().addTextBox("gravity", "Current Gravity", 10, 10, 200, 40, Ogre::ColourValue::Black);
	OutputManager::getSingleton().addTextBox("bounce", "Current Gravity", 10, 50, 200, 40, Ogre::ColourValue::Black);
	OutputManager::getSingleton().addTextBox("friction", "Current Gravity", 10, 90, 200, 40, Ogre::ColourValue::Black);
	OutputManager::getSingleton().addTextBox("score", "Current Score", 10, 130, 200, 40, Ogre::ColourValue::Black);

	if(startAppDirectly){
	commandHandler = new CommandHandler(this); //Else: must use setter
	}

	m_throwInit = false;
	m_CurrentObject = NULL;

	// Set ambient light
    mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5));
	mSceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_ADDITIVE);

    // Create a light
    Ogre::Light* l = mSceneMgr->createLight("MainLight");
    l->setPosition(20,80,0);
	l->setDiffuseColour(0.8,0.9,0.9);
	l->setAttenuation(2900, 1.0, 0.0014, 0.000007);
	
	m_index = 0;

	this->generateEnvironment();

	// create ray scene query
	m_RaySceneQuery = mSceneMgr->createRayQuery(Ogre::Ray());
}

void MultimodalTestApp::generateEnvironment(void)
{
	// define universe plane
	Ogre::Plane universe(Ogre::Vector3::UNIT_Y, -200);

	 // create back plane
    Ogre::MeshManager::getSingleton().createPlane("backPlane", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
												   universe, 500, 500, 10, 10, true, 1, 4, 4, Ogre::Vector3::UNIT_Z);

    Ogre::Entity* entBack = mSceneMgr->createEntity("BackEntity", "backPlane");
	entBack->setMaterialName("MMI/Wall");
    entBack->setCastShadows(false);

	Ogre::SceneNode* backNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();

	backNode->attachObject(entBack);
	backNode->pitch(Ogre::Angle(90));

	// ceiling plane
    Ogre::MeshManager::getSingleton().createPlane("top", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
												   universe, 500, 250, 10, 10, true, 1, 4, 2, Ogre::Vector3::UNIT_Z);

    Ogre::Entity* entTop = mSceneMgr->createEntity("TopEntity", "top");
	entTop->setMaterialName("MMI/Floor");
    entTop->setCastShadows(false);

	Ogre::SceneNode* topNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	topNode->attachObject(entTop);
	topNode->roll(Ogre::Angle(180));
	topNode->setPosition(topNode->getPosition()+Ogre::Vector3(0,-19,-106));

	// floor plane
    Ogre::MeshManager::getSingleton().createPlane("floor", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
												   universe, 500, 250, 10, 10, true, 1, 4, 2, Ogre::Vector3::UNIT_Z);

    Ogre::Entity* entGround = mSceneMgr->createEntity("GroundEntity", "floor");
	entGround->setMaterialName("MMI/Floor");
    entGround->setCastShadows(false);

	Ogre::SceneNode* floorNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	floorNode->attachObject(entGround);
	floorNode->setPosition(floorNode->getPosition()+Ogre::Vector3(0,-19,-106));

	// right side plane
    Ogre::MeshManager::getSingleton().createPlane("right", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
												   universe, 500, 250, 10, 10, true, 1, 4, 2, Ogre::Vector3::UNIT_Z);

    Ogre::Entity* entRight = mSceneMgr->createEntity("RightEntity", "right");
	entRight->setMaterialName("MMI/Wall");
    entRight->setCastShadows(false);

	Ogre::SceneNode* rightNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	rightNode->attachObject(entRight);
	rightNode->roll(Ogre::Angle(90));
	rightNode->setPosition(rightNode->getPosition()+Ogre::Vector3(19,0,-110));

	// left side plane
    Ogre::MeshManager::getSingleton().createPlane("left", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
												   universe, 500, 250, 10, 10, true, 1, 4, 2, Ogre::Vector3::UNIT_Z);

    Ogre::Entity* entLeft = mSceneMgr->createEntity("LeftEntity", "right");
	entLeft->setMaterialName("MMI/Wall");
    entLeft->setCastShadows(false);

	Ogre::SceneNode* leftNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	leftNode->attachObject(entLeft);
	leftNode->roll(Ogre::Angle(-90));
	leftNode->setPosition(leftNode->getPosition()+Ogre::Vector3(-20,0,-110));

	Ogre::Entity* cubeMesh = mSceneMgr->createEntity("cube","cube.mesh");
	cubeMesh->setMaterialName("MMI/Cube");
	cubeMesh->setCastShadows(true);

	Ogre::SceneNode* cubeNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	cubeNode->attachObject(cubeMesh);
	cubeNode->scale(1,1.8,0.1);
	cubeNode->setPosition(cubeNode->getPosition()+Ogre::Vector3(-120,40,-160));

	//create cube
	Ogre::Entity* polyMesh2 = mSceneMgr->createEntity("poly2","cube.mesh");
	polyMesh2->setMaterialName("MMI/Crate");
	polyMesh2->setCastShadows(true);

	Ogre::SceneNode* polyNode2 = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	polyNode2->attachObject(polyMesh2);
	polyNode2->scale(0.8,0.8,0.8);
	polyNode2->setPosition(Ogre::Vector3(70,100,-130));
	polyNode2->roll(Ogre::Degree(45));

	//create cube
	Ogre::Entity* polyMesh3 = mSceneMgr->createEntity("poly3","cube.mesh");
	polyMesh3->setMaterialName("MMI/Crate");
	polyMesh3->setCastShadows(true);

	Ogre::SceneNode* polyNode3 = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	polyNode3->attachObject(polyMesh3);
	polyNode3->scale(0.8,0.8,0.8);
	polyNode3->setPosition(Ogre::Vector3(20,-180,-130));

	//create cube
	Ogre::Entity* polyMesh4 = mSceneMgr->createEntity("poly4","cube.mesh");
	polyMesh4->setMaterialName("MMI/Crate");
	polyMesh4->setCastShadows(true);

	Ogre::SceneNode* polyNode4 = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	polyNode4->attachObject(polyMesh4);
	polyNode4->scale(0.8,0.8,0.8);
	polyNode4->setPosition(Ogre::Vector3(0,-100,-130));
}

bool MultimodalTestApp::mouseMoved(const OIS::MouseEvent &arg)
{
	if (mTrayMgr->injectMouseMove(arg)) return true;
    mCameraMan->injectMouseMove(arg);

	if(m_world->m_currentSphere->isSelected())
	{
		//check if in controll box
		if(m_world->m_currentSphere->getPosition().x > PICKBOX_RIGHT || m_world->m_currentSphere->getPosition().y > PICKBOX_TOP)
		{
			m_world->m_currentSphere->setSelected(false);
			m_world->m_currentSphere->setBoundingBox(false);
			m_tries++;
		}

		//raytrace to cuurent mouse position
		Ogre::Plane plane(Ogre::Vector3::UNIT_Z, -130);

		Ogre::Real screenWidth = Ogre::Root::getSingleton().getAutoCreatedWindow()->getWidth();
		Ogre::Real screenHeight = Ogre::Root::getSingleton().getAutoCreatedWindow()->getHeight();
 
		// convert to 0-1 offset
		Ogre::Real offsetX = arg.state.X.abs / screenWidth;
		Ogre::Real offsetY = arg.state.Y.abs / screenHeight;
 
		// set up the ray
		Ogre::Ray mouseRay = mCamera->getCameraToViewportRay(offsetX, offsetY);

		std::pair<bool, Ogre::Real> result = mouseRay.intersects(plane);
		m_world->m_currentSphere->setMousePosition(mouseRay.getPoint(result.second));
	}
	return true;
}

bool MultimodalTestApp::mouseReleased(const OIS::MouseEvent &arg, OIS::MouseButtonID id)
{
	if (id == OIS::MB_Left)
	{
		m_world->m_currentSphere->setSelected(false);
		m_world->m_currentSphere->setBoundingBox(false);
		m_tries++;
	}
	return true;
}

bool MultimodalTestApp::mousePressed(const OIS::MouseEvent &arg, OIS::MouseButtonID id)
{
	Ogre::Plane plane(Ogre::Vector3::UNIT_Z, -130);

	Ogre::Real screenWidth = Ogre::Root::getSingleton().getAutoCreatedWindow()->getWidth();
	Ogre::Real screenHeight = Ogre::Root::getSingleton().getAutoCreatedWindow()->getHeight();
 
	// convert to 0-1 offset
	Ogre::Real offsetX = arg.state.X.abs / screenWidth;
	Ogre::Real offsetY = arg.state.Y.abs / screenHeight;
 
	// set up the ray
	Ogre::Ray mouseRay = mCamera->getCameraToViewportRay(offsetX, offsetY);

	std::pair<bool, Ogre::Real> result = mouseRay.intersects(plane);

	if (id == OIS::MB_Right)
	{
		if(result.first) 
		{ 
			Ogre::Vector3 point = mouseRay.getPoint(result.second);

			if((point - m_world->m_currentSphere->getPosition()).length() <= 20)
			{
				m_throwInit = true;
			}
		}
		
	}
	else if (id == OIS::MB_Left)
	{
		if(result.first) 
		{ 
			Ogre::Vector3 point = mouseRay.getPoint(result.second);
			
			for(int i = 0; i < m_world->m_sceneElements.size(); i++)
			{
				Ogre::Vector3 spherePosition = m_world->m_sceneElements.at(i)->getPosition();
				Ogre::Real distance = (point-spherePosition).length();

				if(distance <= 20 && m_world->m_currentSphere->getPosition().x < PICKBOX_RIGHT && m_world->m_currentSphere->getPosition().y < PICKBOX_TOP)
				{
					m_world->m_currentSphere->setBoundingBox(false);
					m_world->m_currentSphere = m_world->m_sceneElements.at(i);
					m_world->m_currentSphere->setBoundingBox(true);
					m_world->m_currentSphere->setSelected(true);

					m_world->m_currentSphere->setMousePosition(m_world->m_currentSphere->getPosition());
				}
			}	
		}	
	}
	else if (id == OIS::MB_Middle)
	{
		if(result.first) 
		{ 
			Ogre::Vector3 point = mouseRay.getPoint(result.second);
			m_world->createSphere(point,Ogre::Vector3(0,0,0));

			m_index++;
		}
	}
	
	return true;
}

void MultimodalTestApp::keyPressedForward(const OIS::KeyEvent &arg){
	if (arg.key == OIS::KC_U)   
    {
        for (int i = 0; i < m_world->m_sceneElements.size(); i++)
		{
			m_world->m_sceneElements.at(i)->setGravity(m_world->m_sceneElements.at(i)->getGravity() + 1);
		}
    }
	else if (arg.key == OIS::KC_I)  
    {
        for (int i = 0; i < m_world->m_sceneElements.size(); i++)
		{
			m_world->m_sceneElements.at(i)->setGravity(m_world->m_sceneElements.at(i)->getGravity() - 1);
		}
    }
	else if (arg.key == OIS::KC_O)  
    {
        for (int i = 0; i < m_world->m_sceneElements.size(); i++)
		{
			if(m_world->m_sceneElements.at(i)->getBounce() <= 2)
			{
				m_world->m_sceneElements.at(i)->setBounce(m_world->m_sceneElements.at(i)->getBounce() + 0.02);
			}
		}
    }
	else if (arg.key == OIS::KC_P) 
    {
        for (int i = 0; i < m_world->m_sceneElements.size(); i++)
		{
			if(m_world->m_sceneElements.at(i)->getBounce() >= 0.4)
			{
				m_world->m_sceneElements.at(i)->setBounce(m_world->m_sceneElements.at(i)->getBounce() - 0.02);
			}
		}
    }
	else if(mKeyboard->isKeyDown(OIS::KC_C))
	{
		if(mCameraMan->getStyle()==OgreBites::CS_MANUAL)
		{
			mCameraMan->setStyle(OgreBites::CS_FREELOOK);
		}
		else
		{
			mCameraMan->setStyle(OgreBites::CS_MANUAL);
		}
	}
	else if(mKeyboard->isKeyDown(OIS::KC_L))
	{
		m_world->setGameState(RESET);
		m_tries++;
		
	}
	else if(mKeyboard->isKeyDown(OIS::KC_UP))
	{
		m_world->m_currentSphere->setAcceleration(Ogre::Vector3(0,300,0));
	}
	else if(mKeyboard->isKeyDown(OIS::KC_LEFT))
	{
		m_world->m_currentSphere->setAcceleration(Ogre::Vector3(-300,200,0));
	}
	else if(mKeyboard->isKeyDown(OIS::KC_RIGHT))
	{
		m_world->m_currentSphere->setAcceleration(Ogre::Vector3(300,200,0));
	}
}

void MultimodalTestApp::frameListener(const Ogre::FrameEvent& evt)
{
	// new world step
	m_world->worldStep(evt);

	// update gravity value
	std::stringstream gravityValue;
	gravityValue << "gravity:" << m_world->getPhysics(GRAVITY) << std::endl;
	OutputManager::getSingleton().setText("gravity", gravityValue.str());

	// update friction value
	std::stringstream frictionValue;
	frictionValue << "friction:" << m_world->getPhysics(FRICTION) << std::endl;
	OutputManager::getSingleton().setText("friction", frictionValue.str());

	// update bounce value
	std::stringstream bounceValue;
	bounceValue << "bounce:" << m_world->getPhysics(BOUNCE) << std::endl;
	OutputManager::getSingleton().setText("bounce", bounceValue.str());

	// update score value
	std::stringstream scoreValue;
	scoreValue << "attempts:" << m_tries << std::endl;
	OutputManager::getSingleton().setText("score", scoreValue.str());

	Ogre::Real currentGravity = commandHandler->getPhysics(false,GRAVITY);

	m_world->setPhysics(GRAVITY,currentGravity);
	/*m_world->setPhysics(FRICTION,commandHandler->getPhysics(false,FRICTION));
	m_world->setPhysics(BOUNCE,commandHandler->getPhysics(false,BOUNCE));

	*/m_world->setGameState(commandHandler->getGameState(false));
}

void MultimodalTestApp::setAction(int action)
{
	m_currentAction = action;
}

int MultimodalTestApp::getAction()
{
	return m_currentAction;
}

void MultimodalTestApp::setHandPosition(Ogre::Vector2 handPosition)
{
	m_handPosition = handPosition;
}

Ogre::Vector2 MultimodalTestApp::getHandPosition()
{
	return m_handPosition;
}

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
    INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
    int main(int argc, char *argv[])
#endif
    {
		if(consoleOutput)
			RedirectIOToConsole();
        // Create application object
	
	Wiimote *wiimote;
    wiimote = new Wiimote();
	if (wiimote->open() == false) {
		delete wiimote;
		printf("Could not find Wiimote !!");
		return 0;
	}

	do {
		wiimote->read();
		printf("\r%+d %+d %+d %s %s %s %s %s %s %s %s %s %s %s",
			(wiimote->getXMotion()-0x80),
			(wiimote->getYMotion()-0x80),
			(wiimote->getZMotion()-0x80),
			(wiimote->IsAPressed() ? "A" : " "),
			(wiimote->IsBPressed() ? "B" : " "),
			(wiimote->IsOnePressed() ? "1" : " "),
			(wiimote->IsTwoPressed() ? "2" : " "),
			(wiimote->IsUpPressed() ? "U" : " "),
			(wiimote->IsDownPressed() ? "D" : " "),
			(wiimote->IsLeftPressed() ? "L" : " "),
			(wiimote->IsRightPressed() ? "R" : " "),
			(wiimote->IsMinusPressed() ? "-" : " "),
			(wiimote->IsPlusPressed() ? "+" : " "),
			(wiimote->IsHomePressed() ? "H" : " ")
			);
		Sleep(100);
	} while (wiimote->IsHomePressed() == false);

	wiimote->close();

	delete wiimote;

		/*SingleHandData s1;
		s1.Nui_Init();


       /* MultimodalTestApp app;
		SingleHandData s1= SingleHandData();
		  s1.Nui_Zero();
			s1.Nui_Init();

		// Create console output if wished so
		if(consoleOutput)
			RedirectIOToConsole();

		//Launch application immediately if wished so
		if(startAppDirectly)
        try {
			
            app.go();
        } catch( Ogre::Exception& e ) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
            MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
            std::cerr << "An exception has occured: " <<
                e.getFullDescription().c_str() << std::endl;
#endif
        }
		else{
			CommandHandler *commandHandler = new CommandHandler(&app);
			
		}*/

        return 0;
    }

#ifdef __cplusplus
}
#endif
